前幾天我打開自己的 LINE Cafe Bot,盯著輸入框幾秒,腦中冒出一個有點尷尬的問題:
我要輸入什麼,它才會開始找咖啡廳?
Bot 沒壞,功能其實比以前完整很多。它記得我偏好安靜、有插座的店,可以根據位置推薦附近咖啡廳,也能讓我選好時間再加入 Google Calendar。
問題出在我自己。我做了這些功能,卻還是得回頭翻聊天紀錄,找「查看我的偏好」或「設定我的偏好」到底要怎麼說。
這讓我重新想到一件很基本的事:功能存在,不代表使用者找得到。
於是這次我沒有繼續替 Bot 增加能力,而是幫它補上一個一直看得見的入口——LINE Rich Menu。
前面的 Datetime Picker 開發記錄在這篇:從記住偏好到選好時間:我用 Codex Luna 和 LINE Datetime Picker 把咖啡廳推薦變成真的行程。這次不是再延長功能清單,而是把那些已經做好的東西整理到眼前。
Rich Menu 很容易愈做愈滿。收藏、提醒、換一批、選時間、Google Maps、Calendar,看起來每個功能都值得一格。
但真正適合放在固定選單裡的,應該是「不管對話進行到哪裡都能使用」的入口。最後我只留下四格:
「找附近咖啡」會帶出分享位置的 Quick Reply;「我的偏好」直接查詢目前設定;「設定偏好」則帶入我常用的安靜、有插座、適合工作,並沿用原本的確認機制。
Datetime Picker 沒有放進去。因為它必須知道我正在看哪一次搜尋、點的是哪間店;離開那個情境,單獨開一個時間選擇器沒有意義。它繼續留在咖啡廳推薦卡片上。
這次最大的設計決定,反而是哪些東西不要放。
畫面看起來是四個按鈕,技術上其實是兩層:
2500×1686 的 PNG。例如左上角的「找附近咖啡」:
{
"bounds": {
"x": 0,
"y": 0,
"width": 1250,
"height": 843
},
"action": {
"type": "message",
"label": "找附近咖啡",
"text": "找附近咖啡"
}
}
點擊後,LINE 會替使用者送出「找附近咖啡」,再由原本的 webhook 回覆分享位置按鈕。
圖片和座標必須完全一致。看起來像按了左上角,如果 JSON 寫成右上角的範圍,觸發的就會是另一個功能。Rich Menu 的設計不是把圖畫漂亮就結束,畫面和行為要一起對齊。

手動到 LINE Developers Console 建立一次選單並不難。比較麻煩的是幾週後想換圖,還要重新想起建立、上傳、設成預設的順序。
所以這次順手做了一個管理 CLI。執行:
npm run deploy
程式會自動完成:
如果中途上傳失敗,剛建立但沒有完成的選單會被清掉,原本的預設選單則不受影響。
另外也保留 status、list、create、upload、set-default 和 delete。其中刪除一定要給完整的 Rich Menu ID,避免一句「清掉舊選單」不小心把正在使用的版本也刪掉。
這段程式不華麗,但它讓 Rich Menu 從「我成功設定過一次」,變成「以後可以放心再改」。
第一版 PNG 是合法的 2500×1686,檔案也小於 1 MB,但咖啡杯和問號圖示直接壓在中文字上。
程式不會替美感報錯。要不是把圖片真的打開來看,所有自動檢查都會顯示成功。
我把圖示縮小並往左移,再把標題調到右側,第二次輸出才得到現在的版本。這也成了這次最具體的提醒:視覺功能一定要做視覺驗收。
第一次安裝套件時,npm 發現全域 cache 裡有權限不一致的檔案。它建議用 sudo 修正,但這個專案沒有必要改動整台電腦的設定。
最後改成使用專案自己的 .npm-cache,並放進 .gitignore。安裝環境彼此隔離,問題也就停在這個專案裡。
原本用來把 SVG 轉 PNG 的套件,在 npm audit 出現了高風險的上游公告,而且當下沒有可直接升級的修正版。
即使這裡只處理自己寫的 SVG,我還是把它換成 @resvg/resvg-js。換完後重新跑圖片輸出、TypeScript build、測試和 audit,最後回到零已知漏洞。
我很喜歡這個過程,因為它不是等到安全問題真的發生才修,而是在選擇還很便宜的時候換掉依賴。
這次的程式量不算大,但一開始沒有唯一答案:選單要放哪些入口?哪些功能必須留在對話脈絡裡?圖片要怎麼維護?換版失敗時要怎麼保住舊選單?遇到有風險的套件,要接受、隔離還是替換?
這些問題都不只是「把 API 接起來」,而是需要一路做取捨。
依照 OpenAI 官方模型文件,Sol 適合複雜、開放式,而且需要更多分析、判斷與打磨的工作。這次使用 Sol,我最有感的並不是它寫了多少程式,而是它能把 UI、部署、安全與現有 Bot 的行為放在同一張圖裡考慮。
前一篇 Datetime Picker 的完成條件很清楚,Luna 很適合快速拆解與驗證;這次則有更多「怎樣才算做得剛好」的問題,所以我回到 Sol。
之前我比較習慣從 Console/CLI 和 Codex 互動。這次改用 App,最明顯的差別不是 AI 突然換了一套能力,而是我看待整個開發過程的方式變了。
在 App 裡,對話、檔案、終端輸出、Git 變更和圖片預覽都留在同一個工作空間。第一版選單產生後,我可以直接看見圖示壓住文字,而不是只在終端讀到:
Generated rich-menu.png (405615 bytes)
這行只能證明檔案成功產生,不能證明它好看或好用。
根據 OpenAI 官方 Codex App 指令文件,App 提供檔案搜尋、檔案樹、review diff、內建 terminal 與 browser 等介面。對這種圖片和程式要來回調整的任務,視覺化工作區確實比較順手。
CLI 依然有它很舒服的地方。打開終端、進入專案目錄就能開始工作;如果要把任務接進 script 或 CI,也可以使用 codex exec 做非互動執行,輸出文字或 JSONL。相關用法可以參考 OpenAI 官方 Developer Commands。
對我來說,兩邊的差別可以簡單理解成:
使用 App 不代表不用指令。這次一樣執行了 npm、Git、gcloud 和 LINE API;只是這些操作的結果,不必散落在不同視窗裡。
Rich Menu 沒有使用 Gemini,也沒有增加新的推薦演算法。單看技術亮點,它甚至可能是最近幾次更新裡最樸素的一個。
但現在打開 Cafe Bot,我不用先想起正確句子,也不用翻聊天紀錄。我要找店、看偏好或重新設定,入口就在畫面底部。
有時候產品往前走,不是因為又多會一件事,而是原本會的那些事,終於不再需要被記住。
👉 GitHub:https://github.com/zonawang/line-cafe-rich-menu
👉 更多 LINE Bot 與 AI 實作紀錄: https://github.com/zonawang/zona-ai-learning-lab